﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EasyCLRBase;
using EasyCLRBase.AssemblyExplorer;

namespace CmdOutput
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                new Program();
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR");
                if (e.Data.Contains("LastFile"))
                    Console.WriteLine(e.Data["LastFile"]);
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
            }
        }

        [ImportMany(typeof(IClrBase))]
        private IClrBase[] ClrMethods = null;

        /// <summary>
        /// Erstellt alle notwendige Prozeduren!
        /// </summary>
        /// <param name="path">Pfad zu den anderen CLR.</param>
        private Program()
        {
            ///function, procedure, trigger list
            var fpt = new List<String>();
            var types = new List<String>();
            var aggr = new List<String>();
            String log = String.Concat("Loaded Files: ", Environment.NewLine),
                   currentDir = AppDomain.CurrentDomain.BaseDirectory;// Environment.CurrentDirectory;
            ///list of all loaded assemblies. Need later for creation.
            var assemblyset = new List<AssemblyInfo>();
            ///list of used types. this list is needed to filter for aggregate functions (structs), since export do not support struct
            var usedTypes = new List<Type>();

            using (var catalog = new DirectoryCatalog(currentDir))
            {
                using (var container = new CompositionContainer(catalog))
                {
                    container.ComposeParts(this);

                    foreach (var part in this.ClrMethods)
                    {
                        var es = part.GetClrCreationCode();
                        fpt.AddRange(es.SqlProcedureFunctionTrigger);
                        if (String.IsNullOrEmpty(es.SqlType) == false)
                            types.Add(es.SqlType);
                        if (String.IsNullOrEmpty(es.SqlAggregate) == false)
                            aggr.Add(es.SqlAggregate);
                    }
                    container.Assemblies.ForEach(a => assemblyset.Add(new AssemblyInfo(a)));
                }
            }
            /// Write Log Entries.
            foreach (var _assembly in assemblyset)
            {
                log = String.Concat(log, _assembly.Name, ": ", _assembly.Path, Environment.NewLine);
            }
            String logpath = System.IO.Path.Combine(currentDir, "log.txt");
            System.IO.File.WriteAllText(logpath, log);

            ///Add Assembly
            foreach (var _assembly in assemblyset)
            {
                Console.WriteLine(String.Concat("IF NOT EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'", _assembly.Name, "' and is_user_defined = 1) begin CREATE ASSEMBLY [", _assembly.Name, "] from '", _assembly.Path, "' WITH PERMISSION_SET = UNSAFE end", Environment.NewLine));
            }
            ///Add PDB
            foreach (var _assembly in assemblyset)
            {
                String pdb = System.IO.Path.ChangeExtension(_assembly.Path, "pdb");
                if (System.IO.File.Exists(pdb) == false)
                    continue;
                Console.WriteLine(String.Concat("ALTER ASSEMBLY ", _assembly.Name, " ADD FILE FROM '", pdb, "'", Environment.NewLine));
            }
            ///be aware of that types will be created first, because any other clr types can contain depency on the user defined type.
            this.ConsoleWrite(types);
            this.ConsoleWrite(aggr);
            this.ConsoleWrite(fpt);

            assemblyset = null;
            types = null;
            aggr = null;
            fpt = null;
        }

        private void ConsoleWrite(System.Collections.IEnumerable list)
        {
            foreach (String entry in list)
            {
                Console.WriteLine(String.Concat("exec sp_executesql N'", entry, "'", Environment.NewLine));
            }
        }
    }
}
